package com.smart.tool.core;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Random;
import java.util.Set;
import java.util.StringJoiner;
import java.util.TreeMap;
import java.util.TreeSet;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.function.BiFunction;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.stream.Collectors;

import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;

/**
 * @Author gao.gwq
 * @Version EasyCommonUtils.java, v 0.1 2024年04月24日 21:10 gao.gwq
 * @Description: 通用工具类
 */
public class SmartCommonUtils {


    public static <T,R> R mapTo(T source, Function<T,R> function){
        return mapTo(source,function,null);
    }
    /**
     * 将一个对象转转换为指定的对象,如果source为null 则 返回 defaultVal
     * @param source
     * @param function
     * @param defaultVal
     * example
     *
     * String result = mapTo(100L,param->{
     *           return   param.toString();
     *         },null);
     * @return
     * @param <T>
     * @param <R>
     */
    public static <T,R> R mapTo(T source,Function<T,R> function,R defaultVal){
        return Optional.ofNullable(source)
            .map(function)
            .orElse(defaultVal);
    }
    /**
     * 方法多次重试
     *
     * @param function    具体执行方法
     * @param checkResult 校验结果是否成功 返回boolean ，返回true 为成功
     * @param tryTimes    尝试次数
     * @param intervalMS  睡眠时间 毫秒
     * @param <T>
     * @param <R>
     * @return
     * @throws Exception
     */
    public static <T, R> R executeWithRetry(Function<T, R> function, Function<R, Boolean> checkResult, int tryTimes, long intervalMS) {
        return executeWithRetry(function, checkResult, TimeUnit.MILLISECONDS, tryTimes, intervalMS);
    }

    /**
     * 创建默认是按照 ":" 为分隔符的cache key
     * @param keyPre
     * @param keyElements
     * @return
     * example
     * String cacheKey = buildDefaultCacheKey("redis_pre","1","2");
     * System.out.println(cacheKey); redis_pre:1:2
     */
    public static String buildDefaultCacheKey(String keyPre,String ...keyElements){
        return buildCacheKey(keyPre,":",keyElements);
    }

    /**
     * 构建以split为分割的tair key
     *
     * @example
     * @param keyPre key前缀
     * @param split 分隔符
     * @param keyElements key元素
     * example
     * String cacheKey = buildDefaultCacheKey("redis_pre",":","1","2");
     * System.out.println(cacheKey); redis_pre:1:2
     */
    public static String buildCacheKey(String keyPre,String split,String ...keyElements){
        StringBuilder sb = new StringBuilder();
        if(StringUtils.isNotEmpty(keyPre)){
            sb = new StringBuilder(keyPre);
        }
        for(String keyElement : keyElements){
            if(StringUtils.isEmpty(keyElement)){
                continue;
            }
            sb.append(split).append(keyElement);
        }
        String key = sb.toString();
        if(key.startsWith(split)){
            return key.substring(1, key.length());
        }
        return sb.toString();
    }


    /**
     * 通过重试的方式执行某方法
     *
     * @param function    需要执行的重试防方法体
     * @param checkResult 入参是function返回的结果，用于判断方法是否重试成功, 返回值是 boolean, true 为成功
     * @param timeUnit    重试间隔的时间对象, 通常为秒
     * @param tryTimes    重试次数
     * @param intervalMS  重试间隔的时间
     * example
     *  executeWithRetry(param->{
     *             System.out.println("retry");
     *             return true;
     *         },result->{
     *             if(result){
     *                 return true;
     *             }
     *             return false;
     *         },TimeUnit.SECONDS,3,10);
     */
    public static <T, R> R executeWithRetry(Function<T, R> function, Function<R, Boolean> checkResult, TimeUnit timeUnit, int tryTimes,
        long intervalMS) {
        if (intervalMS <= 0) {
            throw new IllegalArgumentException("intervalMS can not be litter than 0");
        }
        if (tryTimes <= 0) {
            return function.apply(null);
        }
        for (int i = 1; i < tryTimes; i++) {
            try {
                R r = function.apply(null);
                if (checkResult == null) {
                    return r;
                }
                Boolean flag = checkResult.apply(r);
                if (flag == true) {
                    return r;
                }
                timeUnit.sleep(intervalMS);
            } catch (Exception ex) {
                try {
                    timeUnit.sleep(intervalMS);
                } catch (Exception ex1) {

                }
            }
        }
        return function.apply(null);
    }

    /**
     * 堆栈信息转字符串
     *
     * @param trace
     * @return
     */
    public static String traceToString(StackTraceElement[] trace) {
        StringBuilder builder = new StringBuilder(512);
        builder.append("\n");
        for (StackTraceElement traceElement : trace) {
            builder.append("\t ").append(traceElement).append("\n");
        }
        return builder.toString();
    }

    /**
     * 返回几个数组的笛卡尔积的组合
     * @param totalList
     * @return
     * example
     * List<String> list1 = new ArrayList<String>(){{
     *     add("a");
     *     add("b");
     * }};
     * List<String> list2 = new ArrayList<String>(){{
     * add("a");
     * add("b");
     * }};
     * List<String> result = diKaErJi(list1,list2);
     * System.out.println(result); [a, a, b, b]
     *
     */
    public static <T> List<List<T>> diKaErJi(List<T>... totalList) {
        if (totalList == null) {
            throw new IllegalArgumentException("param is null");
        }
        if (totalList.length == 0) {
            throw new IllegalArgumentException("param is empty");
        }
        List<ImmutableSet<T>> immutableSets = Lists.newArrayList();
        for (List<T> tmp : totalList) {
            if (org.apache.commons.collections4.CollectionUtils.isEmpty(tmp)) {
                continue;
            }
            ImmutableSet<T> charList = ImmutableSet.copyOf(tmp);
            immutableSets.add(charList);
        }
        Set<List<T>> sets = Sets.cartesianProduct(immutableSets);
        List<List<T>> result = Lists.newArrayList();
        for (List<T> list : sets) {
            result.add(list);
        }
        return result;
    }

    /**
     * 获取全部的数据,防止出现死循环默认最大1000次,一般用于数据库的数据查询
     *
     * @param startPage
     * @param pageSize
     * @param function
     * @param <T>
     * @return
     *
     * example
     * Param param = new Param();
     * return findByQuery(1,10,(page,limit)->{
     *     param.setPage(page);
     *     param.setLimit(limit);
     *    return xxxMapper.query(param);
     * })
     */

    public static <T> List<T> findByQuery(Integer startPage, Integer pageSize, BiFunction<Integer, Integer, List<T>> function) {
        return findByQuery(1000, startPage, pageSize, function);
    }

    /**
     * @param loopNum   循环次数，防止出现死循环调用
     * @param startPage
     * @param pageSize
     * @param function
     * @param <T>
     * @return
     */
    public static <T> List<T> findByQuery(Integer loopNum, Integer startPage, Integer pageSize,
        BiFunction<Integer, Integer, List<T>> function) {
        if (loopNum <= 0) {
            throw new IllegalArgumentException("loopNum is error");
        }
        if (startPage <= 0) {
            throw new IllegalArgumentException("startPage is error");
        }
        if (pageSize <= 0) {
            throw new IllegalArgumentException("limit is error");
        }
        if (function == null) {
            throw new IllegalArgumentException("function is null");
        }
        List<T> list = new ArrayList<>(16);

        while (true) {
            List<T> result = function.apply(startPage, pageSize);
            if (result == null) {
                break;
            }
            if (result.size() == 0) {
                break;
            }
            list.addAll(result);
            if (result.size() < pageSize) {
                break;
            }
            startPage++;
            if (startPage > loopNum) {
                break;
            }
        }
        return list;
    }

    /**
     * 获取全部的数据,最多查询1000次
     * 将查询到的结果作为入参进行下一步消费
     *
     * @param function
     * @param <T>
     * @return
     */
    @Deprecated
    public static <T> void executeWithQuery(BiFunction<Integer, Integer, List<T>> function, Consumer<List<T>> consumer) {
        executeWithQuery(1000, 1, 200, function, consumer);
    }

    @Deprecated
    public static <T> void executeWithQuery(Integer loopNum, Integer startPage, Integer pageSize,
        BiFunction<Integer, Integer, List<T>> function, Consumer<List<T>> consumer) {
        if (loopNum <= 0) {
            throw new IllegalArgumentException("loopNum is error");
        }
        if (startPage <= 0) {
            throw new IllegalArgumentException("startPage is error");
        }
        if (pageSize <= 0) {
            throw new IllegalArgumentException("limit is error");
        }
        if (function == null) {
            throw new IllegalArgumentException("function is null");
        }

        while (true) {
            List<T> result = function.apply(startPage, pageSize);
            if (result == null) {
                break;
            }
            if (result.size() == 0) {
                break;
            }
            List<T> cloneList = new ArrayList<>(result);
            consumer.accept(cloneList);
            if (result.size() < pageSize) {
                break;
            }
            startPage++;
            if (startPage > loopNum) {
                break;
            }
        }
    }

    /**
     * 对数据量比较大的数据进行批量执行
     *
     * @param elementList
     * @param pageSize 每页的数据
     * @param consumer
     * @param <T>
     *
     *  example
     * List<String> list1 = new ArrayList<String>(){{
     *     add("a");
     *     add("b");
     * batchExecute(list1,1,subList->{
     *     System.out.println(subList.size());
     * })
     *
     */
    public static <T> void batchExecute(Collection<T> elementList, Integer pageSize, Consumer<List<T>> consumer) {
        if (CollectionUtils.isEmpty(elementList)) {
            return;
        }
        int tick = 0;
        if (pageSize == null || pageSize == 0) {
            pageSize = 500;
        }
        List<T> cloneList = new ArrayList<>();
        for (T temp : elementList) {
            cloneList.add(temp);
            tick++;
            if (tick % pageSize == 0) {
                consumer.accept(cloneList);
                cloneList.clear();
            }
        }
        if (!CollectionUtils.isEmpty(cloneList)) {
            consumer.accept(cloneList);
            cloneList.clear();
        }
    }



    /**
     * 对list，根据某个属性进行去重
     *
     * @param cases
     * @param comparator
     * @param <T>
     * @return
     *         List<String> list = new ArrayList<String>(){{
     *             add("a");
     *             add("b");
     *             add("a");
     *              }
     *         };
     *         Comparator<String> comparator = (o1, o2) -> o1.compareTo(o2);
     *         List<String> result = removeDuplicateCase(list,comparator);
     */
    public static <T> List<T> removeDuplicateCase(List<T> cases, Comparator<T> comparator) {
        Set<T> set = new TreeSet<>(comparator);
        set.addAll(cases);
        return new ArrayList<>(set);
    }

    /**
     * 对list进行分组，统计不同分组中的个数
     *
     * @param list
     * @param function
     * @param <K>
     * @param <V>
     * @return
     * example
     *         List<String> list = new ArrayList<String>(){{
     *             add("a");
     *             add("b");
     *             add("a");
     *              }
     *         };
     * Map<String,Integer> countMap = countList(list,obj->{
     *     return obj;
     * });
     */
    public static <K, V> Map<K, Integer> countList(List<V> list, Function<V, K> function) {
        Map<K, Integer> map = Maps.newHashMap();
        if (CollectionUtils.isEmpty(list)) {
            return map;
        }
        for (V v : list) {
            K key = function.apply(v);
            Integer total = map.get(key);
            if (total == null) {
                total = 0;
            }
            total += 1;
            map.put(key, total);
        }
        return map;
    }

    /**
     * 根据key排序map
     * @param map
     * @param comparator
     * @return
     * @param <K>
     * @param <V>
     */
    public static <K, V> Map<K, V> sortMapByKey(Map<K, V> map, Comparator<K> comparator) {
        if (map == null || map.isEmpty()) {
            return null;
        }
        Map<K, V> sortMap = new TreeMap<K, V>(comparator);
        sortMap.putAll(map);
        return sortMap;
    }

    /**
     * 在字符串中查找子字符串的个数
     * @param srcStr
     * @param subStr
     * @return
     */
    public static Integer findSubStrNum(String srcStr, String subStr) {
        if (StringUtils.isEmpty(srcStr) || StringUtils.isEmpty(subStr)) {
            return -1;
        }
        int count = 0;
        int srcLength = srcStr.length();
        int subLength = subStr.length();
        loop1:
        for (int i = 0; i < srcLength; i++) {
            Character srcChar = srcStr.charAt(i);
            Character subChar = subStr.charAt(0);
            if (srcChar.equals(subChar)) {
                boolean flag = true;
                loop2:
                for (int j = 1; j < subLength; j++) {
                    srcChar = srcStr.charAt(i + j);
                    subChar = subStr.charAt(j);
                    if (!srcChar.equals(subChar)) {
                        flag = false;
                        break loop2;
                    }
                }
                if (flag == true) {
                    count++;
                    i = i + subLength - 1;
                }
            }
        }
        return count;
    }

    /**
     * 使用线程池批量执行list中的任务，会根据size进行分组
     * example:
     * List<String> list = Lists.newArrayList("1","2","3","4","5","6","7","8","9","10");
     * int size = 3;
     * currentExecute(list, size, subList -> {
            List<String> cloneList = Lists.newArrayList(subList);
            System.out.println(cloneList);
        }, threadPoolExecutor);
     会分成四组执行
     * @param list
     * @param size
     * @param function
     * @param threadPoolExecutor
     * @return
     * @param <T>
     * @param <R>
     */
    public static <T, R> List<CompletableFuture> currentExecute(Collection<T> list, Integer size, Function<List<T>, R> function,
        ThreadPoolExecutor threadPoolExecutor) {
        if (size <= 0) {
            throw new IllegalArgumentException("size can not littler 0");
        }
        List<CompletableFuture> futureList = Lists.newArrayList();
        batchExecute(list, size, subList -> {
            List<T> cloneList = Lists.newArrayList(subList);
            CompletableFuture future = CompletableFuture.supplyAsync(() -> {
                return function.apply(cloneList);
            }, threadPoolExecutor).exceptionally(ex -> {
                throw new RuntimeException(ex);
            });
            futureList.add(future);
        });
        return futureList;
    }

    /**
     * 将字符串中的字符转换为大写
     * example:
     * String reslut = upperCharacter("hello world", 0, 5);
     * System.out.println(reslut);//HELLO WORLD
     *
     * @param str
     * @param start
     * @param end
     * @return
     */
    public static String upperCharacter(String str, int start, int end) {
        if (str == null || str.trim().length() == 0) {
            throw new IllegalArgumentException("str is null");
        }
        if (start < 0) {
            throw new IllegalArgumentException("start is null");
        }
        if (end < start) {
            throw new IllegalArgumentException("end is litter than start size");
        }
        int length = str.length();
        StringBuilder stringBuilder = new StringBuilder();
        for (int i = 0; i < length; i++) {
            if (i >= start && i <= end) {
                stringBuilder.append(Character.toUpperCase(str.charAt(i)));
                continue;
            }
            stringBuilder.append(str.charAt(i));
        }
        return stringBuilder.toString();
    }

    /*public static Map<String, Object> objectToMap(Object obj) {
        String jsonStr = JSONObject.toJSONString(obj);
        return JSONObject.parseObject(jsonStr, new TypeReference<Map>() {
        });
    }*/

    /**
     * map 转换为对象
     * @param map
     * @param clazz
     * @return
     * @param <T>
     */
    /*public static <T> T mapToBean(Map map, Class<T> clazz) {
        if (map == null) {
            throw new IllegalArgumentException("map is null");
        }
        if (clazz == null) {
            throw new IllegalArgumentException("clazz is null");
        }
        // clazz必须是实体对象，如果是接口，则抛出异常
        if(clazz.isInterface()){
            throw new IllegalArgumentException("clazz is interface");
        }
        //如果是枚举，则抛出异常
        if(clazz.isEnum()){
            throw new IllegalArgumentException("clazz is enum");
        }

        try {
            if(Map.class.isAssignableFrom(clazz)){
                Map tObjMap = (Map)clazz.newInstance();
                map.forEach((key,value)->{
                    tObjMap.put(key,value);
                });
                return (T)tObjMap;
            }
            T tObj = clazz.newInstance();
            BeanUtils.populate(tObj, map);
            return tObj;
        } catch (Exception ex) {
            throw new RuntimeException(ex);
        }
    }*/

    /**
     * 创建堆栈跟踪信息
     * example:
     * String message = createStackTrackMessage(e);
     * System.out.println(message);
     * @param e
     * @return
     */
    public static String createStackTrackMessage(Throwable e) {
        StringBuilder messsage = new StringBuilder();
        if (e != null) {
            messsage.append(e.getClass()).append(": ").append(e.getMessage()).append("\n");
            StackTraceElement[] elements = e.getStackTrace();
            for (StackTraceElement stackTraceElement : elements) {
                messsage.append("\t").append(stackTraceElement.toString()).append("\n");
            }
        }
        return messsage.toString();
    }

    /**
     * 手机号脱敏处理
     * 只保留后四位
     */
    public static String desensitizedPhoneNumber(String phoneNumber) {
        if (StringUtils.isNotBlank(phoneNumber)) {
            phoneNumber = phoneNumber.replaceAll("\\w*(\\w{4})", "*******$1");
        }
        return phoneNumber;
    }

    /**
     * 生成[min,max]质检的随机整数
     *
     * [-10,20] 生成-10 到 20之间的数据
     *
     * [10,20] 生成10 到 20之间的数据
     *
     * @param min 最小的整数
     * @param max 最大的整数
     * @return
     */
    public static int randomInt(int min, int max) {
        if (max < min) {
            throw new IllegalArgumentException("max is less than min");
        }
        int remainder = max - min + 1;
        return new Random().nextInt(max) % (remainder) + min;
    }

    /**
     * 获取缓存
     * example:
     * R result = getIfPresent(key, cacheMap, () -> supplier.get());
     * System.out.println(result);
     *
     * @param key
     * @param cacheMap
     * @param supplier
     * @param <R>
     * @param <K>
     * @return
     */
    public static <R, K> R getIfPresent(K key, Map<K, R> cacheMap, Supplier<R> supplier) {
        if (cacheMap == null) {
            return supplier.get();
        }
        R result = cacheMap.get(key);
        if (result != null) {
            return result;
        }
        result = supplier.get();
        cacheMap.put(key, result);
        return result;
    }

    /**
     * 数组转字符串
     * example:
     * String join = join(",", "1","2","3","4","5","6","7","8","9","10");
     * System.out.println(join);//1,2,3,4,5,6,7,8,9,10
     * @param splitter
     * @param obj
     * @return
     */
    public static String join(String splitter, Object... obj) {

        if(StringUtils.isEmpty(splitter)){
            throw new IllegalArgumentException("splitter is null");
        }

        if (obj == null || obj.length == 0) {
            return StringUtils.EMPTY;
        }

        StringJoiner joiner = new StringJoiner(splitter);
        for (Object o : obj) {
            joiner.add(String.valueOf(o));
        }
        return joiner.toString();
    }

    /**
     * 字符串切割为队列
     * example:
     * String source = "1,2,3,4,5,6,7,8,9,10";
     * List<String> split = split(source, ",");
     * System.out.println(split);//[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
     *
     * @param source source
     * @param splitter splitter-character
     * @return split result
     */
    public static List<String> split(String source, String splitter) {

        if(StringUtils.isEmpty(splitter)){
            throw new IllegalArgumentException("splitter is null");
        }

        if (StringUtils.isBlank(source)) {
            return Collections.emptyList();
        }

        return Arrays.stream(source.trim().split(splitter))
            .map(String::trim)
            .collect(Collectors.toList());
    }
}
